#pragma once

#include <functional>
#include <map>
#include <mutex>
#include <atomic>
#include <vector>
#include <algorithm>

namespace El {
namespace Base {

template<typename R, typename... Args>
class CallbackList {
public:
    using Callback = std::function<R(Args...)>;

    struct CallbackInfo {
        Callback callback;
        int priority;
    };

    CallbackList() : nextHandle_(0) {}

    int32_t Register(const Callback &callback, int32_t priority = 100)
    {
        std::lock_guard<std::mutex> lock(mutex_);
        int32_t handle = nextHandle_++;
        callbacks_[handle] = {callback, priority};
        return handle;
    }

    void Unregister(int32_t handle)
    {
        std::lock_guard<std::mutex> lock(mutex_);
        callbacks_.erase(handle);
    }

    auto InvokeAll(Args... args)
    {
        std::lock_guard<std::mutex> lock(mutex_);
        std::vector<std::pair<int32_t, CallbackInfo>> sorted_callbacks(callbacks_.begin(), callbacks_.end());
        std::sort(sorted_callbacks.begin(), sorted_callbacks.end(),
                 [](const auto& a, const auto& b) {
                     return a.second.priority < b.second.priority;
                 });
        
        if constexpr (std::is_same_v<R, void>) {
            for (const auto &pair : sorted_callbacks) {
                pair.second.callback(std::forward<Args>(args)...);
            }
        } else if constexpr (std::is_same_v<R, bool>) {
            bool result = true;
            for (const auto &pair : sorted_callbacks) {
                if (!pair.second.callback(std::forward<Args>(args)...)) {
                    result = false;
                    break;
                }
            }
            return result;
        } else {
            static_assert(std::is_same_v<R, void> || std::is_same_v<R, bool>,
                         "Callback must return void or bool");
        }
    }

    bool IsEmpty() const
    {
        std::lock_guard<std::mutex> lock(mutex_);
        return callbacks_.empty();
    }

    void Clear()
    {
        std::lock_guard<std::mutex> lock(mutex_);
        callbacks_.clear();
    }

private:
    std::map<int32_t, CallbackInfo> callbacks_;
    std::atomic<int32_t> nextHandle_;
    mutable std::mutex mutex_;
};

} // namespace Base
} // namespace El